<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Language" content="en" />
    <title>s6-linux-init: the s6-linux-init-maker program</title>
    <meta name="Description" content="s6-linux-init: the s6-linux-init-maker program" />
    <meta name="Keywords" content="s6 linux administration root init maker" />
    <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
  </head>
<body>

<p>
<a href="index.html">s6-linux-init</a><br />
<a href="http://skarnet.org/software/">Software</a><br />
<a href="http://skarnet.org/">skarnet.org</a>
</p>

<h1> The <tt>s6-linux-init-maker</tt> program </h1>

<p>
<tt>s6-linux-init-maker</tt> reads configuration options on
the command line, and outputs a directory to place in the
root filesystem. That directory contains a script suitable
as an init program, as well as support file hierarchies to
get a complete
<a href="http://skarnet.org/software/s6/">s6</a>
infrastructure running when the system is booted on that
script.
</p>

<p>
 s6-linux-init-maker only writes scripts. At boot time, these
scripts will call commands provided by other skarnet.org packages
such as
<a href="http://skarnet.org/software/execline/">execline</a> or
<a href="http://skarnet.org/software/s6/">s6</a>. It is the
responsibility of the administrator to make sure that all the
dependencies are properly installed at boot time, and that the
correct options have been given to s6-linux-init-maker so that
the programs are found <em>on the root filesystem of the
machine</em> - else the scripts will crash.
</p>

</p>

<h2> Interface and usage </h2>

<pre>
     s6-linux-init-maker \
       [ -c <em>basedir</em> ] \
       [ -l <em>tmpfsdir</em> ] \
       [ -b <em>execline_bindir</em> ] \
       [ -u <em>log_uid</em> -g <em>log_gid</em> | -U ] \
       [ -G <em>early_getty</em> ] \
       [ -2 <em>stage2</em> ] \
       [ -r ] \
       [ -Z ] <em>stage2_finish</em> \
       [ -3 <em>stage3</em> ] \
       [ -p <em>initial_path</em> ] \
       [ -m <em>initial_umask</em> ] \
       [ -t <em>timestamp_style</em> ] \
       [ -d <em>dev_style</em> ] \
       [ -s <em>env_store</em> ] \
       [ -e <em>initial_envvar</em> ] ... \
       <em>dir</em>
</pre>

<ul>
 <li> s6-linux-init-maker should be run as root. </li>
 <li> s6-linux-init-maker parses options on its command line. </li>
 <li> It writes data into a directory <em>dir</em>, which must not
exist beforehand. </li>
 <li> It exits 0 if everything went well, 100 if a user error occurred,
and 111 if a problem occurred during the creation of the directory
or its contents. </li>
</ul>

<p>
 <em>dir</em> should then be copied by the administrator to the place
declared as <em>basedir</em>. Be careful: it contains fifos, files with
precise uid/gid permissions, and files with non-standard access rights,
so be sure to copy it verbatim. The
<a href="http://skarnet.org/software/s6-portable-utils/s6-hiercopy.html">s6-hiercopy</a>
tool can do it, as well as the GNU or busybox <tt>cp -a</tt> or <tt>mv</tt> commands.
</p>

<p>
 The <tt><em>basedir</em>/init</tt> script
is then suitable as a "stage 1" init program, i.e. the first program
run by the kernel. The administrator should make a symbolic link
from <tt>/sbin/init</tt> to <tt><em>basedir</em>/init</tt>; the
machine will then be ready to boot 
</p>

<h2> Boot sequence </h2>

<p>
 When the kernel boots, it runs the <tt><em>basedir</em>/init</tt> script,
also known as <em>stage 1</em>. and this is what happens:
</p>

<ul>
 <li> <em>stage 1</em> is an
<a href="http://skarnet.org/software/execline/">execline</a> script, so
the first process run by the kernel is the
<a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a>
program launcher. </li>
 <li> <em>stage 1</em> mounts a
<a href="https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt">tmpfs</a>
filesystem on <em>tmpfsdir</em>. </li>
 <li> <em>stage 1</em> copies <tt><em>basedir</em>/run-image</tt> verbatim to
<em>tmpfsdir</em>. </li>
 <li> <em>stage 1</em> empties its environment, then reads a global set of environment variables from the
<tt><em>basedir</em>/env</tt>
<a href="http://skarnet.org/software/s6/s6-envdir.html">environment directory</a>. </li>
 <li> <em>stage 1</em> forks a child that will block until
<a href="http://skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is running. </li>
 <li> <em>stage 1</em> executes, as process 1, into
<a href="http://skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>,
with <tt><em>tmpfsdir</em>/service</tt> as a
<a href="http://skarnet.org/software/s6/scandir.html">scan directory</a>. </li>
 <li> This scan directory already contains at least one service, which is the
<em>catch-all logger</em>: error messages from the supervision tree, and
from services that do not have a dedicated logger, are handled by a
special <a href="http://skarnet.org/software/s6/s6-log.html">s6-log</a>
instance and made available in <tt><em>tmpfsdir</em>/uncaught-logs</tt>
instead of clogging the system console. </li>
 <li> If the <tt>-G</tt> option has been given to s6-linux-init-maker, the
scan directory will also contain a service for an early getty. </li>
 <li> s6-svscan starts all the services defined in the scan directory,
and unblocks the child forked by <em>stage 1</em>. </li>
 <li> This child executes into <em>stage2</em>. </li>
</ul>

<p>
 <em>stage2</em> is the responsibility of the administrator - it will
not be written automatically!
It should
contain all the necessary initialization sequence to bring up a proper
system. When <em>stage2</em> is executed, the machine state is as follows:
</p>

<ul>
 <li> <em>stage2</em>'s working directory is <tt>/</tt> and its stdin
is <tt>/dev/null</tt>. Its
stdout and stderr both point either to <tt>/dev/console</tt> or to the pipe
to the catch-all logger, depending on the <tt>-r</tt> option. </li>
 <li> The system has a valid device directory mounted on <tt>/dev</tt>. </li>
 <li> Depending on the kernel boot command line, the root filesystem
may be in read-only mode. </li>
 <li> There is a tmpfs available for root only in <em>tmpfsdir</em>. </li>
 <li> <a href="http://skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
is running as process 1. At any time, it is possible to make it supervise a long-lived
process by linking the appropriate
<a href="http://skarnet.org/software/s6/servicedir.html">service directory</a>
into <tt><em>tmpfsdir</em>/service</tt>, then running the command
<tt>s6-svscanctl -a <em>tmpfsdir</em>/service</tt>. Services without a
dedicated logger will send their output to the catch-all logger. </li>
 <li> A getty service may already be available. The point of this early
getty is essentially to make it easier to debug if <em>stage2</em> fails. </li>
</ul>

<p>
 There is <em>nothing else</em>. In particular, no filesystem has been
mounted yet, including <tt>/proc</tt> and <tt>/sys</tt>; and no one-time
initialization
has been performed. The point of <em>stage 1</em> is only to make it
possible to run <em>stage2</em> with a logging infrastructure and a
supervision infrastructure already available, and all the
real machine and service initialization should happen in <em>stage2</em>.
</p>

<h2> Shutdown sequence </h2>

<ul>

 <li> A shutdown is performed when the administrator runs one of the
<a href="s6-halt.html">s6-halt</a>,
<a href="s6-poweroff.html">s6-poweroff</a> or
<a href="s6-reboot.html">s6-reboot</a> commands. </li>

 <li> Those commands send a signal to the
<a href="http://skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
process running as pid 1; this signal is caught and s6-svscan runs the
corresponding "signal handler" script that has been placed by
s6-linux-init-maker into the
<tt><em>basedir</em>/run-image/service/.s6-svscan</tt> directory (and that
has been copied at boot time to <tt><em>tmpfsdir</em>/service/.s6-svscan</tt>). </li>

 <li> That script first spawns the <em>stage2_finish</em> script, who
must have been written by the administrator. The purpose of
<em>stage2_finish</em> is to perform the high-level shutdown sequence
while the supervision tree is still alive. Typically, when using a
service manager, <em>stage2_finish</em> would tell the service manager
to bring all services down. When using
<a href="http://skarnet.org/software/s6-rc/">s6-rc</a>, a typical
<em>stage2_finish</em> script just contains <tt>s6-rc -da change</tt>.
 More generally speaking, <em>stage2_finish</em> should undo what
<em>stage2</em> has done at boot time. </li>

 <li> The "signal handler" script then tells s6-svscan to exit via an
appropriate <a href="http://skarnet.org/software/s6/s6-svscanctl.html">s6-svscanctl</a>
command: s6-svscan then executes into the <em>stage3</em> script, which, like
<em>stage2</em> and <em>stage2_finish</em>, is the responsibility of the
administrator. When <em>stage3</em> runs, the machine is in the following
state:

<ul>
 <li> The supervision tree has been torn down: it is not operational
anymore. (So, commands such as
<a href="http://skarnet.org/software/s6-rc/s6-rc.html">s6-rc</a>, which
require a live supervision tree, will not work.)
 <li> <em>stage3</em> runs as process 1. Doing so makes it easier to recover
after killing all processes by <tt>kill -9 -1</tt> or
<a href="http://skarnet.org/software/s6-portable-utils/s6-nuke.html">s6-nuke</a>. </li>
 <li> Its working directory is <tt>/</tt> and its stdin is <tt>/dev/null</tt> </li>
 <li> Its stdout and stderr are both <tt>/dev/console</tt> </li>
 <li> Depending on the exact configuration and what the administrator has
written in <em>stage2_finish</em>, there may or may not be
long-running services that remain alive. The catch-all logger and its
supervisor will <em>always</em> be alive; this is not a problem because they
do not hold any file descriptor to a filesystem that would need to be
unmounted. </li>
</ul> </li>

<li> The last command that <em>stage3</em> executes should be
<tt>s6-$1 -f</tt>, <tt>$1</tt> being the first argument that has been
given to it. This command will instantly execute the hard system halt,
poweroff or reboot that has initially been asked by the admin. </li>

</ul>

<p>
 The <tt>examples/</tt> subdirectory of the s6-linux-init package
contains an example of <tt>/etc/rc.init</tt>, <tt>/etc/rc.tini</tt>
and <tt>/etc/rc.shutdown</tt> scripts, suitable for
<em>stage2</em>, <em>stage2_finish</em> and <em>stage3</em>
respectively. Those scripts can practically be used as is if the machine
is managed by the <a href="http://skarnet.org/software/s6-rc/">s6-rc</a>
service manager.
</p>

<h2> s6-linux-init-maker options </h2>

<ul>
 <li> <tt>-c</tt>&nbsp;<em>basedir</em>&nbsp;: at boot time, <em>stage 1</em>,
which should be accessible as <tt><em>basedir</em>/init</tt>,
will read its read-only data from <em>basedir</em>. After running
s6-linux-init-maker, the administrator should make sure to copy the
created directory <em>dir</em> to <em>basedir</em>. <em>basedir</em>
must be absolute. Default is
<strong><tt>/etc/s6-linux-init</tt></strong>. </li> <p />

 <li> <tt>-l</tt>&nbsp;<em>tmpfsdir</em>&nbsp;: at boot time, a tmpfs will
be mounted on <em>tmpfsdir</em>. The directory should already exist in
the root filesystem, and be empty. <em>tmpfsdir</em> must be absolute. Default is
<strong><tt>/run</tt></strong>. </li> <p />

 <li> <tt>-b</tt>&nbsp;<em>execline_bindir</em>&nbsp;: init is run by the kernel
without a PATH, and since it is a script, it is necessary to tell it where
to find the
<a href="http://skarnet.org/software/execline/execlineb.html">execlineb</a>
launcher and the first few early commands before PATH can be set.
<em>execline_bindir</em> is the location where the execline binaries can be
found. It must be absolute. Default is
<strong><tt>/bin</tt></strong>. </li> <p />

 <li> <tt>-u</tt>&nbsp;<em>log_uid</em>&nbsp;: the catch-all
logger will run with the uid <em>log_uid</em>. Default is 0. <li> <p />

 <li> <tt>-g</tt>&nbsp;<em>log_gid</em>&nbsp;: the catch-all
logger will run with the gid <em>log_gid</em>. Default is 0. <li> <p />

 <li> <tt>-U</tt>&nbsp;: the correct <em>log_uid</em> and
<em>log_gid</em> values for the catch-all logger will be read from the
UID and GID environment variables that have been passed to
s6-linux-init-maker. This allows for invocations such as
<tt>s6-envuidgid nobody s6-linux-init-maker -U ...</tt> so that
the catch-all logger runs as the <tt>nobody</tt> user. Be aware that
this option is only safe when the user database on the
<em>boot-time</em> machine is the same as on the <em>run-time</em>
machine, else the catch-all logger may run with an unexpected uid
and gid. </li>

 <li> <tt>-G</tt>&nbsp;<em>early_getty</em>&nbsp;: if this option
is set, s6-linux-init-maker will define a service that will run
very early, before <em>stage2</em> is executed. This early service
should be a getty, to allow logins even if <em>stage2</em> fails.
<em>early_getty</em> should be a simple command line: for instance,
<tt>"/sbin/getty 38400 tty1"</tt>. By default, no early service
is defined. </li> <p />

 <li> <tt>-2</tt>&nbsp;<em>stage2</em>&nbsp;: <em>stage2</em> is
the location of the stage 2 script that will be run when the
system has an operational supervision tree. It must be absolute. Default is
<strong><tt>/etc/rc.init</tt></strong>. </li> <p />

 <li> <tt>-r</tt>&nbsp;: redirect. By default, <em>stage2</em> is
run with stdout and stderr pointing to <tt>/dev/console</tt>, so that
users can see what init scripts print. However, it may conflict
with an early getty, or be undesirable for other reasons. The
<tt>-r</tt> option redirects <em>stage2</em>'s stdout and stderr
to the catch-all logger, so the output will be made available
in the <tt><em>tmpfsdir</em>/uncaught-logs</tt> directory. </li> <p />

 <li> <tt>-Z</tt>&nbsp;<em>stage2_finish</em>&nbsp;:
<em>stage2_finish</em> is the location of the script that will be
run when s6-svscan receives a signal that tells it to stop the
machine, before it executes into <em>stage3</em>. It must be
absolute. Default is <strong><tt>/etc/rc.tini</tt></strong>. </li> <p />

 <li> <tt>-3</tt>&nbsp;<em>stage3</em>&nbsp;: <em>stage3</em> is
the location of the stage 3 script that will be run at the end of
the machine lifetime, when s6-svscan is told to terminate.
It must be absolute. Default is
<strong><tt>/etc/rc.shutdown</tt></strong>. </li> <p />

 <li> <tt>-p</tt>&nbsp;<em>initial_path</em>&nbsp;: the value to
set the PATH environment variable to, for all the starting processes.
This will be done as early as possible in <em>stage 1</em>. It is
absolutely necessary for
<a href="http://skarnet.org/software/execline/">execline</a>,
<a href="http://skarnet.org/software/s6/">s6</a>,
<a href="http://skarnet.org/software/s6-portable-utils/">s6-portable-utils</a> and
<a href="http://skarnet.org/software/s6-linux-utils/">s6-linux-utils</a>
binaries to be accessible via <em>initial_path</em>, else the machine
will not boot. Default is
<strong><tt>/usr/bin:/bin</tt></strong>. </li> <p />

 <li> <tt>-m</tt>&nbsp;<em>initial_umask</em>&nbsp;: the value of
the initial file umask for all the starting processes, in octal.
Default is
<strong><tt>022</tt></strong>. </li> <p />

 <li> <tt>-t</tt>&nbsp;<em>timestamp_style</em>&nbsp;: how
logs are timestamped by the catch-all logger. 0 means no
timestamp, 1 means
<a href="http://cr.yp.to/libtai/tai64.html">external TAI64N format</a>,
2 means
<a href="http://www.iso.org/iso/home/standards/iso8601.htm">ISO 8601 format</a>,
and 3 means both. Default is
<strong><tt>1</tt></strong>. </li> <p />

 <li> <tt>-d</tt>&nbsp;<em>dev_style</em>&nbsp;: how <tt>/dev</tt> is
handled on this system. 0 means a static <tt>/dev</tt>, 1 means
devtmpfs but not automounted by the kernel at boot time, and 2 means
devtmpfs automounted by the kernel at boot time. Default is
<strong><tt>2</tt></strong>. </li> <p />

 <li> <tt>-s</tt>&nbsp;<em>env_store</em>&nbsp;: stage 1 init sometimes
inherits a few environment variables from the kernel. It empties its
environment before spawning stage2 and executing into s6-svscan, in
order to prevent those "kernel" environment variables from leaking
into the whole process tree. However, sometimes those variables are
needed at a later time; in that case, giving the <tt>-s</tt> option
to s6-linux-init-maker makes stage 1 init dump the "kernel" environment
variables into the <em>env_store</em> directory, via the
<a href="http://skarnet.org/software/s6-portable-utils/s6-dumpenv.html">s6-dumpenv</a>
program, before erasing them. <em>env_store</em> should obviously be
a writable directory, so it should be located under <em>tmpfsdir</em>!
If this option is not given (which is the default), the environment
inherited from the kernel isn't saved anywhere. </li> <p />

 <li> <tt>-e</tt>&nbsp;<em>initial_envvar</em>&nbsp;: this option
can be repeated. For every <em>initial_envvar</em>, s6-linux-init-maker
will adjust the global environment directory in <em>dir</em>/env.
<em>initial_envvar</em> must either be of the form <em>VAR</em>,
to make sure that <em>VAR</em> does not appear in the global
environment, or of the form <em>VAR=VALUE</em>, to add an
environment variable <em>VAR</em> with the value <em>VALUE</em>.
The TZ variable, for instance, is a good candidate to be set in
the global environment. </li> <p />
</ul>

<h2> Notes </h2>

<p>
 The difficult parts of
<a href="http://skarnet.org/software/s6/s6-svscan-1.html">running
s6-svscan as process 1</a> are:
</p>

<ul>
 <li> The fact that the supervision tree requires writable directories,
so in order to accommodate read-only root filesystems, there needs to
be a tmpfs mounted before s6-svscan is run. </li>
 <li> The catch-22 coming for the need to redirect the supervision
tree's output away from <tt>/dev/console</tt> (which is fine for a
first process invocation but impractical for log management of a
whole process tree) and into a logger that is itself managed by the
supervision tree it's reading data from. </li>
</ul>

<p>
 The main benefit of s6-linux-init-maker is that it automates those
parts. This means that it has been designed for <em>real hardware</em>
where the above issues apply.
 If you are building an init system for a
virtual machine, a container, or anything similar that does not
have the <tt>/dev/console</tt> issue or the read-only rootfs issue,
you will probably not reap much benefit from using s6-linux-init-maker:
you could probably invoke
<a href="http://skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
directly as your process 1, or build a script by hand, which
would result in a simpler init with less dependencies.
</p>

</body>
</html>
